#include "ShadowMapPass.h"
#include "RenderScene.h"
#include <Common.h>
#include <Interface.h>
#include "Utils.h"
#include <glm/ext.hpp>
#include <GLM/gtc/type_ptr.hpp>

CShadowMapPass::CShadowMapPass(const std::string& vPassName, int vExecutionOrder) : IRenderPass(vPassName, vExecutionOrder)
{
}

void CShadowMapPass::initV()
{
	m_pShader = std::make_shared<CShader>("Shaders/ShadowMap.vs", "Shaders/ShadowMap.fs", "Shaders/ShadowMap.gs");

	auto TextureConfig4Depth = std::make_shared<Graphics::STexture>();
	TextureConfig4Depth->TextureType = Graphics::STexture::ETextureType::TextureCubeMap;
	TextureConfig4Depth->InternalFormat = GL_DEPTH_COMPONENT32F;
	TextureConfig4Depth->ExternalFormat = GL_DEPTH_COMPONENT;
	TextureConfig4Depth->DataType = GL_FLOAT;
	TextureConfig4Depth->Width = TextureConfig4Depth->Height = m_ShadowMapResolution;
	TextureConfig4Depth->Type4MinFilter = GL_NEAREST;
	TextureConfig4Depth->Type4MagFilter = GL_NEAREST;
	TextureConfig4Depth->Type4WrapS = TextureConfig4Depth->Type4WrapT = TextureConfig4Depth->Type4WrapR = GL_CLAMP_TO_EDGE;
	//TextureConfig4Depth->BorderColor = { 0,0,0,0 };
	TextureConfig4Depth->TextureAttachmentType = Graphics::STexture::ETextureAttachmentType::DepthTexture;
	genTexture(TextureConfig4Depth);

	// m_FBO = genFBO({ TextureConfig4Depth }); // bug!
	glGenFramebuffers(1, &(GLuint&)m_FBO);
	glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, TextureConfig4Depth->TextureID, 0);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
	{
		std::cerr << "Error::FBO:: Framebuffer Is Not Complete." << std::endl;
	}

	Graphics::ResourceManager::registerSharedData("DepthTexture", TextureConfig4Depth);

	m_pShader->activeShader();

	glm::vec3 LightPos(0.0f, 0.0f, 2.0f);
	Graphics::ResourceManager::registerSharedData("LightPos", LightPos);
	m_pShader->setFloatUniformValue("u_LightPos", LightPos.x, LightPos.y, LightPos.z);

	float NearPlane = Graphics::Camera::getMainCameraNear();
	float FarPlane = Graphics::Camera::getMainCameraFar();
	m_pShader->setFloatUniformValue("u_FarPlane", FarPlane);

	glm::mat4 ShadowProjection = glm::perspective(glm::radians(90.0f), 1.0f, NearPlane, FarPlane);
	std::vector<glm::mat4> ShadowTransforms;
	ShadowTransforms.push_back(ShadowProjection * glm::lookAt(LightPos, LightPos + glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	ShadowTransforms.push_back(ShadowProjection * glm::lookAt(LightPos, LightPos + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	ShadowTransforms.push_back(ShadowProjection * glm::lookAt(LightPos, LightPos + glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)));
	ShadowTransforms.push_back(ShadowProjection * glm::lookAt(LightPos, LightPos + glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)));
	ShadowTransforms.push_back(ShadowProjection * glm::lookAt(LightPos, LightPos + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	ShadowTransforms.push_back(ShadowProjection * glm::lookAt(LightPos, LightPos + glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	for (int i = 0; i < 6; i++)
	{
		m_pShader->setMat4UniformValue("u_ShadowMatrices[" + std::to_string(i) + "]", glm::value_ptr(ShadowTransforms[i]));
	}
}

void CShadowMapPass::updateV()
{
	glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glViewport(0, 0, m_ShadowMapResolution, m_ShadowMapResolution);

	m_pShader->activeShader();
	renderScene(m_pShader);

	glViewport(0, 0, Graphics::WINDOW_KEYWORD::getWindowWidth(), Graphics::WINDOW_KEYWORD::getWindowHeight());
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
